﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Principal;
using System.Web;

namespace System.Testing
{
    /// <summary>
    /// Module for allowing tester to authenticate on behalf of other user
    /// In order to activate it this module must be added to web.config\configuration\system.webserver\modules
    /// and TestParameterService must override "TestUserIdParameter" parameter
    /// TestParameterService must be initialized in the module
    /// </summary>
    public class TestAuthenticationOverrideModule : IHttpModule
    {
        public void Dispose()
        {
        }


        public void Init(HttpApplication context)
        {
            OnInitializing(context);
            context.PostAuthenticateRequest += context_PostAuthenticateRequest;
            OnInitialized(context);
        }

        protected virtual void OnInitialized(HttpApplication context)
        {
        }

        protected virtual void OnInitializing(HttpApplication context)
        {
        }

        void context_PostAuthenticateRequest(object sender, EventArgs e)
        {
            try
            {
                var testUserId = TestUserIdParameter.GetOverridableValue(null as string);
                if (string.IsNullOrEmpty(testUserId)) return;

                var originalUser = HttpContext.Current.User;
                if (originalUser != null && !(originalUser is TestUser))
                {
                    HttpContext.Current.User = CreateTestUser(testUserId, originalUser);
                }
            }
            catch (Exception exc)
            {
                Trace.TraceError(exc.ToString());
            }
        }

        /// <summary>
        /// Overridable test parameter used by module to retrieve test user name.
        /// TestService override handler should take overriden value from Cookie or from Session of the user
        /// </summary>
        public readonly static TestParameter TestUserIdParameter = new TestParameter("TestUserId");

        /// <summary>
        /// Creating target test user by userId and original principle
        /// </summary>
        /// <param name="testUserId"></param>
        /// <param name="originalUser"></param>
        /// <returns></returns>
        protected virtual TestUser CreateTestUser(string testUserId, IPrincipal originalUser)
        {
            return new TestUser(testUserId, originalUser);
        }
    }

    /// <summary>
    /// Base class for test user
    /// </summary>
    public class TestUser : IPrincipal, IIdentity
    {
        public string TargetUserId {get;protected set;}

        public TestUser(string targetUserId, IPrincipal originalPrincipal)
        {
            this.TargetUserId = targetUserId;
            this.OriginalPrinciple = originalPrincipal;
        }
        public IPrincipal OriginalPrinciple { get; protected set; }


        public virtual string AuthenticationType
        {
            get { return OriginalPrinciple.Identity.AuthenticationType; }
        }

        public virtual bool IsAuthenticated
        {
            get { return OriginalPrinciple.Identity.IsAuthenticated; }
        }

        public virtual string Name
        {
            get { return TargetUserId; }
        }


        IIdentity IPrincipal.Identity
        {
            get { return this; }
        }

        public virtual bool IsInRole(string role)
        {
            return OriginalPrinciple.IsInRole(role);
        }
    }

    public class TestWindowsUser:TestUser
    {
        public TestWindowsUser(string testUserId, IPrincipal originalPrincipal):base(testUserId, originalPrincipal)
        {
            TargetWindowsIdentity = new WindowsIdentity(testUserId);
            TargetWindowsPrincipal = new WindowsPrincipal(TargetWindowsIdentity);
        }

        public WindowsPrincipal TargetWindowsPrincipal { get; protected set; }
        public WindowsIdentity TargetWindowsIdentity { get; protected set; }

        public override string Name
        {
            get { return TargetWindowsIdentity.Name; }
        }

        public virtual IEnumerable<IdentityReference> Groups
        {
            get { return TargetWindowsIdentity.Groups; }
        }
        public override bool IsInRole(string role)
        {
            return TargetWindowsPrincipal.IsInRole(role);
        }
    }

}